home *** CD-ROM | disk | FTP | other *** search
-
- {*******************************************************}
- { }
- { Delphi Visual Component Library }
- { BDE Provider }
- { }
- { Copyright (c) 1997 Borland International }
- { }
- {*******************************************************}
-
- unit BdeProv;
-
- interface
-
- uses Windows, SysUtils, Classes, DB, DBTables, DSIntf, DBClient, Provider, BDE;
-
-
- type
-
- { EUpdateError }
-
- EUpdateError = class(EDatabaseError)
- private
- FContext: string;
- FPreviousError: Integer;
- public
- constructor Create(NativeError, Context: string;
- ErrorCode, PrevError: DBIResult);
- property Context: string read FContext;
- property PreviousError: Integer read FPreviousError;
- end;
-
- { TProvider }
-
- TResolverResponse = (rrSkip, rrAbort, rrMerge, rrApply, rrIgnore);
- TResolverErrorEvent = procedure(DataSet: TClientDataSet; E: EUpdateError;
- UpdateKind: TUpdateKind; var Response: TResolverResponse) of object;
- TDataRequestEvent = function(Sender: TObject; Input: OleVariant): OleVariant of object;
-
- TProvider = class(TCustomProvider)
- private
- FDataSet: TDBDataSet;
- FDelta: OleVariant;
- FTableParam: string;
- FSQLParam: string;
- FDeltaDS: TClientDataSet;
- FMaxErrors: Integer;
- FFirstPacketSent: Boolean;
- FDataSetActive: Boolean;
- FHitEOF: Boolean;
- FBeforeGetData: TNotifyEvent;
- FAfterGetData: TNotifyEvent;
- FBeforeUpdate: TNotifyEvent;
- FAfterUpdate: TNotifyEvent;
- FOnUpdateError: TResolverErrorEvent;
- FOnDataRequest: TDataRequestEvent;
- function UpdateCallback(iRslt: Integer; iUpdateKind: DSAttr;
- iResAction: dsCBRType; iErrCode: Integer; pErrMessage, pErrContext: PChar;
- pRecUpd, pRecOrg, pRecConflict: Pointer): dsCBRType; stdcall;
- procedure SetParamValues;
- protected
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- function Get_Data: OleVariant; override;
- public
- constructor Create(AOwner: TComponent); override;
- function ApplyUpdates(Delta: OleVariant; MaxErrors: Integer; out ErrorCount: Integer): OleVariant; override;
- function GetRecords(Count: Integer; out RecsOut: Integer): OleVariant; override;
- function DataRequest(Input: OleVariant): OleVariant; override;
- procedure Reset; override;
- property Data;
- property Provider;
- published
- property Constraints default True;
- property DataSet: TDBDataSet read FDataSet write FDataSet;
- property BeforeGetData: TNotifyEvent read FBeforeGetData write FBeforeGetData;
- property AfterGetData: TNotifyEvent read FAfterGetData write FAfterGetData;
- property BeforeUpdate: TNotifyEvent read FBeforeUpdate write FBeforeUpdate;
- property AfterUpdate: TNotifyEvent read FAfterUpdate write FAfterUpdate;
- property OnDataRequest: TDataRequestEvent read FOnDataRequest write FOnDataRequest;
- property OnUpdateError: TResolverErrorEvent read FOnUpdateError write FOnUpdateError;
- end;
-
- implementation
-
- uses DBCommon, Forms, StdVCL, BDEConst, ActiveX;
-
- type
- TProviderOptions = (poNoUpdate, poConstraints, poFieldOrg, poMetaData);
- TResolverDataSet = class(TClientDataSet); { ! Get rid of this }
-
- { EUpdateError }
-
- constructor EUpdateError.Create(NativeError, Context: string;
- ErrorCode, PrevError: DBIResult);
- begin
- FContext := Context;
- FPreviousError := PrevError;
- inherited Create(NativeError);
- end;
-
- { TProvider }
-
- function CreateProvider(Source: TDBDataSet): IProvider;
- begin
- with TProvider.Create(Source) do
- begin
- Result := Provider;
- DataSet := Source;
- end;
- end;
-
- constructor TProvider.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- if AOwner is TDBDataSet then
- FDataSet := TDBDataSet(AOwner);
- Constraints := True;
- end;
-
- procedure TProvider.Notification(AComponent: TComponent; Operation: TOperation);
- begin
- inherited Notification(AComponent, Operation);
- if (Operation = opRemove) and (FDataSet <> nil) and
- (AComponent = FDataSet) then FDataSet := nil;
- end;
-
- function TProvider.Get_Data: OleVariant;
- var
- RecsOut: Integer;
- begin
- Result := GetRecords(-1, RecsOut);
- end;
-
- function TProvider.GetRecords(Count: Integer; out RecsOut: Integer): OleVariant;
- var
- DataPacket: PVarArray;
- TempEOF: Bool;
- ProvOpts: set of TProviderOptions;
- begin
- if not Assigned(FDataSet) then DatabaseError(SMissingDataSet);
- if Assigned(FBeforeGetData) then FBeforeGetData(Self);
- ProvOpts := [];
- if not FFirstPacketSent then
- begin
- FDataSetActive := FDataSet.Active;
- Include(ProvOpts, poMetaData);
- if Constraints then Include(ProvOpts, poConstraints);
- FHitEOF := False;
- FFirstPacketSent := True;
- end;
- FDataSet.Active := True;
- try
- FDataSet.CheckBrowseMode;
- if FHitEOF then
- DbiSetToEnd(FDataSet.Handle)
- else if (Count = -1) and (poMetaData in ProvOpts) then
- DbiSetToBegin(FDataSet.Handle)
- else
- begin
- FDataSet.UpdateCursorPos;
- DbiGetPriorRecord(FDataSet.Handle, dbiNoLock, nil, nil);
- end;
- RecsOut := Count;
- Check(DsProviderGetDataPacket(FDataSet.Handle, Integer(Byte(ProvOpts)),
- @TDBDataSet.ConstraintCallBack, Integer(FDataset), @RecsOut, DataPacket,
- TempEOF));
- Result := SafeArrayToVariant(DataPacket);
- if (RecsOut <> Count) then Reset else
- begin
- FHitEOF := DbiGetNextRecord(FDataSet.Handle, dbiNoLock, nil, nil) = DBIERR_EOF;
- FDataSet.CursorPosChanged;
- FDataSet.Resync([]);
- end;
- except
- Reset;
- end;
- if Assigned(FAfterGetData) then FAfterGetData(Self);
- end;
-
- procedure TProvider.Reset;
- begin
- if FFirstPacketSent then
- begin
- DataSet.Active := FDataSetActive;
- if Assigned(DataSet) and DataSet.Active then DataSet.First;
- FFirstPacketSent := False;
- end;
- end;
-
- procedure TProvider.SetParamValues;
- var
- Len: Integer;
- begin
- if FDataSet is TQuery then
- begin
- FSQLParam := TQuery(FDataSet).SQL.Text;
- FTableParam := '';
- end else if FDataSet is TTable then
- begin
- Len := Length(TTable(FDataSet).TableName);
- if Len > 0 then
- begin
- SetLength(FTableParam, Len);
- AnsiToNative(FDataSet.Locale, TTable(FDataSet).TableName, PChar(FTableParam), Len);
- end;
- FSQLParam := '';
- end;
- end;
-
- function TProvider.ApplyUpdates(Delta: OleVariant; MaxErrors: Integer; out ErrorCount: Integer): OleVariant;
- var
- Results: PacketList;
- Callback: Pointer;
- Session: TSession;
- FDatabase: TDatabase;
- DeltaList: PacketList;
- Callbacks: CBList;
- ClientData: IntList;
- SQLData: NameList;
- TableData: NameList;
- MoreCB: CBList;
- begin
- if not Assigned(FDataSet) then DatabaseError(SMissingDataSet);
- if Assigned(FBeforeUpdate) then FBeforeUpdate(Self);
- SetParamValues;
- FDelta := Delta;
- Session := Sessions.OpenSession(FDataSet.SessionName);
- FDatabase := Session.OpenDatabase(FDataSet.DatabaseName);
- try
- try
- FMaxErrors := MaxErrors;
- ErrorCount := FMaxErrors;
- if Assigned(FOnUpdateError) then
- Callback := @TProvider.UpdateCallback else
- Callback := nil;
- DeltaList[1] := TVarData(FDelta).VArray;
- Callbacks[1] := Callback;
- ClientData[1] := Integer(Self);
- TableData[1] := PChar(FTableParam);
- SQLData[1] := PChar(FSQLParam);
- MoreCB[1] := @TDBDataSet.ConstraintCallBack;
- Check(DsResolver(1, @DeltaList, FDatabase.Handle, nil, nil, nil, @SQLData,
- @TableData, @ClientData, @Callbacks, @MoreCB, Integer(FDataSet),
- @ErrorCount, @Results));
- Result := SafeArrayToVariant(Results[1]);
- finally
- FDelta := NULL;
- FDeltaDS.Free;
- FDeltaDS := nil;
- end;
- finally
- Session.CloseDatabase(FDatabase);
- end;
- if Assigned(FAfterUpdate) then FAfterUpdate(Self);
- end;
-
- function TProvider.DataRequest(Input: OleVariant): OleVariant;
- begin
- if Assigned(FOnDataRequest) then
- Result := FOnDataRequest(Self, Input) else
- Result := NULL;
- end;
-
- function TProvider.UpdateCallback(
- iRslt : Integer; { Previous error message if any }
- iUpdateKind : DSAttr; { Update request Insert/Modify/Delete }
- iResAction : dsCBRType; { Resolver response (Not used here) }
- iErrCode : Integer; { Native error-code, (BDE or ..) }
- pErrMessage, { Native errormessage, if any (otherwise NULL) }
- pErrContext : PChar; { 1-level error context, if any (otherwise NULL) }
- pRecUpd, { Record that failed update }
- pRecOrg, { Original record, if any }
- pRecConflict : Pointer { Conflicting record, if any }
- ): dsCBRType;
- var
- Response: TResolverResponse;
- UpdateKind: TUpdateKind;
- begin
- try
- if not Assigned(FDeltaDS) then
- begin
- FDeltaDS := TClientDataSet.Create(Self);
- FDeltaDS.Data := FDelta;
- end;
- TResolverDataSet(FDeltaDS).SetAltRecBuffers(pRecOrg, pRecUpd, pRecConflict);
- if iUpdateKind = dsRecDeleted then
- UpdateKind := ukDelete
- else if iUpdateKind = dsRecNew then
- UpdateKind := ukInsert
- else
- UpdateKind := ukModify;
-
- if FMaxErrors > 0 then
- Response := rrSkip else
- Response := rrAbort;
- try
- raise EUpdateError.Create(pErrMessage, pErrContext, iErrCode, iRslt);
- except
- on E: EUpdateError do
- FOnUpdateError(FDeltaDS, E, UpdateKind, Response);
- end;
- except
- Application.HandleException(Self);
- Response := rrAbort;
- end;
- Result := Ord(Response) + 1;
- end;
-
- begin
- if not Assigned(CreateProviderProc) then
- CreateProviderProc := CreateProvider;
- end.
-